package src.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.Polygon;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import src.view.ViewHandler.DisplayType;
import src.view.ViewHandler.ImageType;
import src.view.MainWindow;
import src.model.gameWorld.MapTileConfiguration;
import src.model.gameWorld.GameDataObject;
import src.model.gameObject.GameObjectType;
import src.event.events.MapUpdateEvent;
import src.event.events.CursorChangeEvent;
import src.event.EventListener;
import src.model.gameWorld.PlayerMap;
import src.model.gameWorld.Position;

/**
 * <b>Overview</b>
 * The AreaViewport handles the view of the map as the player sees it.
 *
 * <b>Responsibilities</b>
 * Display a portion of the player's map in the main view.
 *
 * <b>Collaborators</b>
 * Drawable - Client: the Drawable canvas must display the graphical representation of the AreaViewport.
 *
 * <b>Implementor</b> - Bruce
 * <b>Tester</b> - Tim
 *
 * @author Bruce
 */
public class AreaViewport extends Viewport implements EventListener<MapUpdateEvent>
{

	private HashMap<String, BufferedImage> imgLib;

	private PlayerMap playerMap;

	private MapTileConfiguration tileConfig;

	private int xOffset;
	private int yOffset;

	private int tileWidth;
	private int tileHeight;

	private Position cursor;

	private JFrame imgObs;

	/**
	 * Creates a new StatusViewport.
	 *
	 * @param canvas the Drawable object on which to display the status view.
	 */
	public AreaViewport(ViewHandler vh, Drawable canvas, MapTileConfiguration config)
	{
		super(vh, canvas, DisplayType.MAIN);
		imgObs = (JFrame)canvas;
		xOffset = 1;
		yOffset = 1;
		cursor = new Position(0, 0);
		tileConfig = config;
	}

	public Position getCursorPos()
	{
		return cursor;
	}

	public void setMapTileConfiguration(MapTileConfiguration mtc){
		this.tileConfig = mtc;
	}

	public void setTileWidth(int tileWidth){
		this.tileWidth = tileWidth;
	}

	public void setTileHeight(int tileHeight){
		this.tileHeight = tileHeight;
	}

	/* (non-Javadoc)
	 * @see src.view.Viewport#draw()
	 */
	@Override
	protected void draw()
	{
		if(playerMap == null)
			return;

		if(tileConfig == MapTileConfiguration.Hexagonal)
			drawHex();

		if(tileConfig == MapTileConfiguration.Octagonal)
			drawOctagonal();
	}

	private void drawHex(){
		getCanvas().setClip(0, 0, getWidth(), getHeight());
		getCanvas().setColor(Color.BLACK);
		getCanvas().fillRect(0, 0, getWidth(), getHeight());

		for(int row = 0; row < Position.getXBoundary(); row++){
			for(int col = 0; col < Position.getYBoundary(); col++){
				Polygon poly = new Polygon();

				//Things for easy math:
				double size = 50.0;
				int sidelength = tileWidth / 2;
				int horizedgedist = ((int)(size * Math.cos(Math.toRadians(37.5))));
				int vertedgedist = ((int)(size * Math.sin(Math.toRadians(37.5))));

				int x, y;

				//First coord
				x = col * (sidelength + horizedgedist) + horizedgedist + xOffset + col + row;
				y = row * (2 * vertedgedist) + vertedgedist*col + col + row;
				poly.addPoint(x, y);

				//Second coord
				x += sidelength;
				poly.addPoint(x, y);

				//Third coord
				x += horizedgedist;
				y += vertedgedist;
				poly.addPoint(x, y);

				//Fourth coord
				x -= horizedgedist;
				y += vertedgedist;
				poly.addPoint(x, y);

				//Fifth coord
				x -= sidelength;
				poly.addPoint(x, y);

				//Sixth coord
				x -= horizedgedist;
				y -= vertedgedist;
				poly.addPoint(x, y);

				ArrayList<GameDataObject> GOs = playerMap.GetObjectsAtPosition(new Position(row, col));

				if(GOs != null)
				{
					BufferedImage img;

					for(GameDataObject go : GOs)
						if(go.getType() == GameObjectType.TERRAIN)
						{
							getCanvas().setClip(poly);
							img = getImage(ImageType.valueOf(go.getFilepath()));
							getCanvas().drawImage(img, x, y - 50, null);
						}
					for(GameDataObject go : GOs)
						if(go.getType() == GameObjectType.UNIT)
						{
							getCanvas().setClip(x, y - 50, sidelength * 3, sidelength * 3);
							img = getImage(ImageType.valueOf(go.getFilepath()));
							getCanvas().drawImage(img, x, y - 50, null);
						}
				}

				int alpha = 0;
				switch(playerMap.getTileStatus(new Position(col, row))){
					case Unseen:
						alpha = 255;
						break;
					case seen:
						alpha = 120;
						break;
					case seeing:
						alpha = 30;
						break;
				}
				getCanvas().setClip(poly);
				getCanvas().setColor(new Color(0,0,0,alpha));
				getCanvas().fillRect(x, y - 50, tileWidth * 3, tileHeight * 3);
			}
		}
	}




	private void drawOctagonal(){
		getCanvas().setColor(Color.BLUE);
		getCanvas().fillRect(0, 0, getWidth(), getHeight());

		for(int row = 0; row < Position.getXBoundary(); row++){
			for(int col = 0; col < Position.getYBoundary(); col++){
				int beginX = row + xOffset + row*tileWidth;
				int beginY = col + yOffset + col*tileHeight;

				ArrayList<GameDataObject> GOs = playerMap.GetObjectsAtPosition(new Position(row, col));
				/*Display things in the following order:
				  1. TERRAIN
				  2. Area Effect
			  	  3. UNIT/structure
				  4. ITEM
				  5. ???
				  6. PROFIT!
				 */

				BufferedImage img;

				if(GOs != null)
				{
					for(GameDataObject go : GOs)
						if(go.getType() == GameObjectType.TERRAIN)
						{
							img = getImage(ImageType.valueOf(go.getFilepath()));
							getCanvas().drawImage(img, beginX, beginY, tileWidth, tileHeight, null);
						}
					for(GameDataObject go : GOs)
						if(go.getType() == GameObjectType.UNIT)
						{
							img = getImage(ImageType.valueOf(go.getFilepath()));
							getCanvas().drawImage(img, beginX, beginY, tileWidth, tileHeight, null);
						}
				}

				int alpha = 0;
				switch(playerMap.getTileStatus(new Position(col, row))){
					case Unseen:
						alpha = 255;
						break;
					case seen:
						alpha = 120;
						break;
					case seeing:
						alpha = 30;
						break;
				}
				getCanvas().setColor(new Color(0,0,0,alpha));
				getCanvas().fillRect(row + xOffset + row * tileWidth, col + yOffset + col * tileHeight, tileWidth, tileHeight);

			}
		}

		getCanvas().setColor(new Color(200, 100, 100, 100));
		getCanvas().fillRect(cursor.X() + xOffset + cursor.X() * tileWidth, cursor.Y() + yOffset + cursor.Y() * tileHeight, tileWidth, tileHeight);
	}

	/* (non-Javadoc)
	 * @see src.view.Viewport#getResizeDimension()
	 */
	@Override
	protected Dimension getResizeDimension()
	{
		return new Dimension(getCanvasDimensions().width - 200, getCanvasDimensions().height - 200);
	}

	/* (non-Javadoc)
	 * @see src.view.Viewport#getResizePoint()
	 */
	@Override
	protected Point2D getResizePoint()
	{
		return Point2D.origin();
	}

	public void click(Point p)
	{
		switch(tileConfig)
		{
			case Octagonal:
			{
				int x = (int)((p.getX() - 5) / tileWidth);
				int y = (int)((p.getY() + 5) / tileHeight);
				ViewEventHandler.fireEvent(new CursorChangeEvent(x, y));
			}
		}
	}

	public boolean eventCallback(MapUpdateEvent e){
		this.playerMap = e.getMap();
		refresh(DisplayType.MAIN);
		return true;
	}

	public void select(int x, int y)
	{
		cursor.setX(x);
		cursor.setY(y);
		refresh(DisplayType.MAIN);
	}

	public String toString()
	{
		return "Area";
	}
}